#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common.h"
#include "tda_synth.h"
#include "modform.h"

float (* p_function_mod[FUNCTION_MOD_CANT])(float,float,float,float)={mod_linear,mod_exp,mod_quartsin,mod_halfsin,mod_log,mod_tri,mod_constant,mod_sin,mod_pulses,mod_invlinear,mod_invexp,mod_quartcos,mod_halfcos,mod_invlog,};

char * function_mod_name[]={
	"LINEAR",
	"EXP",
	"QUARTSIN",
	"HALFSIN",
	"LOG",
	"TRI",
	"CONSTANT",
	"SIN",
	"PULSES",
	"INVLINEAR",
	"INVEXP",
	"QUARTCOS",
	"HALFCOS",
	"INVLOG",
};

status_t convert_string_to_function_mod_t(char * s, function_mod_t * function){
	
	size_t i;
	
	if(s==NULL || function==NULL) return ERROR_NULL_POINTER;
	
	for(i=0;i<MAX_FUNCTION_NAME;i++){
		if(!strcmp(s,function_mod_name[i])){
			*function=(function_mod_t)i;
			return OK;
		}
	}
	return ERROR_INVALID_DATA;
}

status_t adt_synth_new (adt_synth_t ** pp_adt_synth, FILE * synth_data){
	
	size_t i,j;
	status_t status;
	
	if(pp_adt_synth == NULL || synth_data==NULL) return ERROR_NULL_POINTER;
	
	if((*pp_adt_synth=(adt_synth_t*)malloc(sizeof(adt_synth_t)))==NULL) return ERROR_MEMORY;
	
	adt_synth_set_harmonics_qty(*pp_adt_synth,0);
	(*pp_adt_synth)->harmonics=NULL;
	
	for(i=0;i<MAX_MOD_STEPS;i++){
		adt_synth_set_function(*pp_adt_synth,NULL,i);
		for(j=0;j<MAX_ARGS;j++){
			adt_synth_set_function_arg(*pp_adt_synth,0,i,j);
		}
	}
	
	if((status=adt_synth_load_data(*pp_adt_synth,synth_data))!=OK){
		adt_synth_destroy(pp_adt_synth);
		return status;	
	}
	
	return OK;
}

status_t adt_synth_load_data (adt_synth_t * p_adt_synth, FILE * fi){
	
	function_mod_t function;
	status_t status;
	string_t line;
	char * p_aux;
	char aux_c;
	int aux_int;
	float aux_float;
	size_t i,j;
	
	if( p_adt_synth==NULL || fi==NULL) return ERROR_NULL_POINTER;
	/*carga de cantidad de armonicos*/
	if(fgets(line,MAX_STRING,fi)==NULL) return ERROR_INVALID_DATA;
	aux_int=strtol(line,&p_aux,10);
	if(((aux_c=(*p_aux))!=STR_END && aux_c!=CHAR_LF && aux_c!=CHAR_CR && aux_c!=CHAR_TAB && aux_c!=CHAR_SPACE) || aux_int < 0) return ERROR_INVALID_DATA;
	if((status=adt_synth_set_harmonics_qty(p_adt_synth, aux_int))!=OK){
		return status;	
	}
	
	/*reserva de memoria para armonicos*/
	if((p_adt_synth->harmonics=(harmonic_struct_t *)malloc(aux_int*sizeof(harmonic_struct_t)))==NULL) return ERROR_MEMORY;
	/*carga de armonicos*/
	
	for(i=0;i<aux_int;i++){

		if(fgets(line,MAX_STRING,fi)==NULL) return ERROR_INVALID_DATA;
		aux_float=strtod(line,&p_aux);
		if(*p_aux!=CHAR_SPACE || aux_float < 0) return ERROR_INVALID_DATA;
		if((status=adt_synth_set_harmonic_multiplier(p_adt_synth,aux_float,i))!=OK){
			return status;	
		}
		
		aux_float=strtod(p_aux,&p_aux);
		if(((aux_c=(*p_aux))!=STR_END && aux_c!=CHAR_LF && aux_c!=CHAR_CR && aux_c!=CHAR_TAB && aux_c!=CHAR_SPACE) || aux_float < 0) return ERROR_INVALID_DATA;
		if((status=adt_synth_set_harmonic_intensity(p_adt_synth,aux_float,i))!=OK){
			return status;	
		}
	}
	
	/*carga de funciones de modulacion*/
	for(i=0;i<MAX_MOD_STEPS;i++){
		
		if(fgets(line,MAX_STRING,fi)==NULL) return ERROR_INVALID_DATA;
		if((p_aux=strtok(line,TOKENS_END_LINE))==NULL) return ERROR_INVALID_DATA;
		if((convert_string_to_function_mod_t(p_aux,&function))!=OK) return ERROR_INVALID_DATA;
		switch(i){
			case ATTACK:
				if(function<MOD_STEP_ATTACK_MIN || function>MOD_STEP_ATTACK_MAX) return ERROR_INVALID_DATA;
				break;
			case SUSTAIN:
				if(function<MOD_STEP_SUSTAIN_MIN || function>MOD_STEP_SUSTAIN_MAX) return ERROR_INVALID_DATA;
				break;
			case DECAY:
				if(function<MOD_STEP_DECAY_MIN || function>MOD_STEP_DECAY_MAX) return ERROR_INVALID_DATA;
				break;
		}
		if((status=adt_synth_set_function(p_adt_synth,p_function_mod[function],i))!=OK){
			return status;	
		}
		for(j=0;(p_aux=strtok(NULL,TOKENS_END_LINE))!=NULL;j++){
		
			aux_float=strtod(p_aux,&p_aux);
			if((*p_aux)!=STR_END || aux_float < 0 || j>=MAX_ARGS) return ERROR_INVALID_DATA;
			if((status=adt_synth_set_function_arg(p_adt_synth,aux_float,i,j))!=OK){
				return status;	
			}
		}
	}
	return OK;
}

status_t adt_synth_destroy(adt_synth_t ** pp_adt_synth){

	if(pp_adt_synth==NULL) return ERROR_NULL_POINTER;
	
	if ((*pp_adt_synth)->harmonics!=NULL){
		free((*pp_adt_synth)->harmonics);
	}
	free(*pp_adt_synth);
	pp_adt_synth=NULL;
	return OK;
}

status_t adt_synth_set_harmonics_qty(adt_synth_t * p_adt_synth,int qty){
	
	if(p_adt_synth==NULL) return ERROR_NULL_POINTER;
	if(qty<=0) return ERROR_INVALID_DATA;
	
	p_adt_synth->harmonics_qty=qty;
	return OK;
}
status_t adt_synth_set_harmonic_multiplier(adt_synth_t * p_adt_synth,float multiplier,size_t n_harmonic){
	
	if(p_adt_synth==NULL) return ERROR_NULL_POINTER;
	if(multiplier<=0 || n_harmonic<0) return ERROR_INVALID_DATA;
	
	p_adt_synth->harmonics[n_harmonic].multiplier=multiplier;
	return OK;
}

status_t adt_synth_set_harmonic_intensity(adt_synth_t * p_adt_synth,float intensity,size_t n_harmonic){

	if(p_adt_synth==NULL) return ERROR_NULL_POINTER;
	if(intensity<0 || n_harmonic<0) return ERROR_INVALID_DATA;	
	p_adt_synth->harmonics[n_harmonic].intensity=intensity;
	return OK;
}

status_t adt_synth_set_function(adt_synth_t * p_adt_synth, float (*function)(float,float,float,float),mod_step_t mod_step){

	if(p_adt_synth==NULL || function==NULL) return ERROR_NULL_POINTER;
	if(mod_step>=MAX_MOD_STEPS || mod_step<0) return ERROR_INVALID_DATA;
	
	p_adt_synth->mod_steps[mod_step].function_mod=function;
	return OK;
}

status_t adt_synth_set_function_arg(adt_synth_t * p_adt_synth,float arg,mod_step_t mod_step,size_t n_arg){

	if(p_adt_synth==NULL) return ERROR_NULL_POINTER;
	if(n_arg>=MAX_ARGS || n_arg<0 || mod_step>=MAX_MOD_STEPS || mod_step<0) return ERROR_INVALID_DATA;
	
	p_adt_synth->mod_steps[mod_step].args[n_arg]=arg;
	return OK;
}

int adt_synth_get_harmonics_qty(adt_synth_t * p_adt_synth){
	
	return p_adt_synth->harmonics_qty;
}
float adt_synth_get_harmonic_multiplier(adt_synth_t * p_adt_synth,size_t n_harmonic){
	
	return p_adt_synth->harmonics[n_harmonic].multiplier;
}
float adt_synth_get_harmonic_intensity(adt_synth_t * p_adt_synth,size_t n_harmonic){
	
	return p_adt_synth->harmonics[n_harmonic].intensity;
}
float(* adt_synth_get_function(adt_synth_t * p_adt_synth,mod_step_t mod_step))(float,float,float,float){
	
	return p_adt_synth->mod_steps[mod_step].function_mod;
}
float adt_synth_get_function_arg(adt_synth_t * p_adt_synth,mod_step_t mod_step,size_t n_arg){
	
	return p_adt_synth->mod_steps[mod_step].args[n_arg];
}
